/*
 * FinanceFrame.java
 *
 *  
 */
package org.msb.finance.app;

import static org.msb.finance.app.ActionConstants.*;

import java.awt.BorderLayout;
import java.awt.HeadlessException;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.prefs.Preferences;

import javax.swing.*;

import org.msb.finance.data.Account;
import org.msb.finance.data.Cabinet;
import org.msb.finance.models.AccountTableModel;

/**
 * 
 * 
 * @author Marc Boudreau
 * 
 */
@SuppressWarnings("serial")
public class FinanceFrame extends JFrame implements FinanceFileChangeListener {

	/**
	 * 
	 */
	private Preferences prefs;

	/**
	 * 
	 */
	private Map<Object, Action> actions;

	/*
	 * 
	 */
	private JComponent contentPane;

	/*
	 * 
	 */
	private JTabbedPane tabPane;

	/*
	 * 
	 */
	private Cabinet cabinet;

	/*
	 * 
	 */
	private AccountTableModel accountsTableModel;

	/**
	 * @throws HeadlessException
	 */
	public FinanceFrame() throws HeadlessException {
		super();

		FinanceApp.addFinanceFileChangeListener(this);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.swing.JFrame#frameInit()
	 */
	@Override
	protected void frameInit() {
		super.frameInit();

		createActions();

		final Action exitAction = this.actions.get(ActionConstants.FILE_EXIT);
		addWindowListener(new WindowAdapter() {

			/*
			 * (non-Javadoc)
			 * 
			 * @see java.awt.event.WindowAdapter#windowClosed(java.awt.event.WindowEvent)
			 */
			@Override
			public void windowClosing(WindowEvent e) {
				exitAction.actionPerformed(new ActionEvent(e.getSource(), e.getID(), "exit", //$NON-NLS-1$
						System.currentTimeMillis(), 0));
			}
		});

		setContentPane(createContentPane());
		setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
		setExtendedState(getPreviousState());
		setIconImage(FinanceApp.getMessageResources().getImage("frame.title.icon")); //$NON-NLS-1$
		setJMenuBar(createMenuBar());
		setLocation(getPreviousBounds().getLocation());
		setSize(getPreviousBounds().getSize());
		setTitle(FinanceApp.getMessageResources().getString("frame.title.caption")); //$NON-NLS-1$
	}

	/**
	 * 
	 */
	private void createActions() {
		if (null == this.actions) {
			this.actions = new HashMap<Object, Action>();
		}

		Resources res = FinanceApp.getMessageResources();
		ActionListener mainHandler = new MainActionHandler();

		this.actions.put(FILE_NEW, new Action(mainHandler, res, "action.file.new", "new", true, false, null)); //$NON-NLS-1$ //$NON-NLS-2$
		this.actions.put(FILE_OPEN, new Action(mainHandler, res, "action.file.open", "open", //$NON-NLS-1$ //$NON-NLS-2$
				true, false, null));
		this.actions.put(FILE_PROPERTIES, new Action(null, res, "action.file.properties", "properties", false, false, //$NON-NLS-1$ //$NON-NLS-2$
				null));
		this.actions.put(FILE_EXIT, new Action(new ExitActionHandler(), res,
				"action.file.exit", "exit", true, false, null)); //$NON-NLS-1$ //$NON-NLS-2$
		this.actions.put(ACCOUNT_NEW, new Action(mainHandler, res, "action.account.new", "newaccount", true //$NON-NLS-1$ //$NON-NLS-2$
				/*
				 * This needs to be changed to false and an ActionEnabledCallback object that monitors if there is an
				 * open file needs to be put in place.
				 */, false, null));
	}

	/**
	 * 
	 * @return
	 */
	private JComponent createContentPane() {
		this.contentPane = new JPanel(new BorderLayout());

		return this.contentPane;
	}

	/**
	 * 
	 * @return
	 */
	private JMenuBar createMenuBar() {
		JMenuBar menubar = new JMenuBar();

		JMenu fileMenu = new JMenu(new Action(null, FinanceApp.getMessageResources(), "menu.file", null, true, false, //$NON-NLS-1$
				null));

		JMenuItem fileNewMenu = new JMenuItem(this.actions.get(ActionConstants.FILE_NEW));
		fileMenu.add(fileNewMenu);

		JMenuItem fileOpenMenu = new JMenuItem(this.actions.get(ActionConstants.FILE_OPEN));
		fileMenu.add(fileOpenMenu);

		fileMenu.addSeparator();

		JMenuItem filePropertiesMenu = new JMenuItem(this.actions.get(ActionConstants.FILE_PROPERTIES));
		fileMenu.add(filePropertiesMenu);

		fileMenu.addSeparator();

		JMenuItem fileExitMenu = new JMenuItem(this.actions.get(ActionConstants.FILE_EXIT));
		fileMenu.add(fileExitMenu);

		menubar.add(fileMenu);

		JMenu accountMenu = new JMenu(new Action(null, FinanceApp.getMessageResources(), "menu.account", null, true, //$NON-NLS-1$
				false, null));

		JMenuItem accountNewMenu = new JMenuItem(this.actions.get(ActionConstants.ACCOUNT_NEW));
		accountMenu.add(accountNewMenu);

		menubar.add(accountMenu);

		return menubar;
	}

	/**
	 * 
	 * @return
	 */
	private JComponent createTabPane() {
		this.tabPane = new JTabbedPane(SwingConstants.TOP, JTabbedPane.SCROLL_TAB_LAYOUT);
		this.tabPane.addTab(
				FinanceApp.getMessageResources().getString("frame.tabs.accounts.title"), createAccountsPanel()); //$NON-NLS-1$

		return this.tabPane;
	}

	/**
	 * 
	 * @return
	 */
	private JComponent createAccountsPanel() {
		return new AccountsPanel(this, this.cabinet);
	}

	/**
	 * 
	 * @return
	 */
	private Rectangle getPreviousBounds() {
		if (null == this.prefs) {
			this.prefs = Preferences.userNodeForPackage(FinanceApp.class);
		}

		int x = this.prefs.getInt("FinanceFrame.Location.X", 0); //$NON-NLS-1$
		int y = this.prefs.getInt("FinanceFrame.Location.Y", 0); //$NON-NLS-1$
		int height = this.prefs.getInt("FinanceFrame.Size.Height", 600); //$NON-NLS-1$
		int width = this.prefs.getInt("FinanceFrame.Size.Width", 800); //$NON-NLS-1$

		return new Rectangle(x, y, width, height);
	}

	/**
	 * 
	 * @return
	 */
	private int getPreviousState() {
		if (null == this.prefs) {
			this.prefs = Preferences.userNodeForPackage(FinanceApp.class);
		}

		return this.prefs.getInt("FinanceFrame.State", MAXIMIZED_BOTH); //$NON-NLS-1$
	}

	/**
	 * 
	 */
	private void rememberState() {
		this.prefs.putInt("FinanceFrame.Location.X", getX()); //$NON-NLS-1$
		this.prefs.putInt("FinanceFrame.Location.Y", getY()); //$NON-NLS-1$
		this.prefs.putInt("FinanceFrame.Size.Height", getHeight()); //$NON-NLS-1$
		this.prefs.putInt("FinanceFrame.Size.Width", getWidth()); //$NON-NLS-1$
		this.prefs.putInt("FinanceFrame.State", getExtendedState()); //$NON-NLS-1$
	}

	/*
	 * 
	 */
	private void removeTabPane() {
		this.contentPane.remove(this.tabPane);
		this.contentPane.validate();
	}

	/*
	 * 
	 */
	private void addTabPane() {
		this.contentPane.add(this.tabPane, BorderLayout.CENTER);
		this.contentPane.validate();
	}

	/**
	 * 
	 * @return s
	 */
	public AccountTableModel createAccountTableModel() {
		return this.accountsTableModel = new AccountTableModel();
	}

	/*
	 * 
	 */
	private class MainActionHandler implements ActionListener {

		/*
		 * 
		 */
		private JFileChooser fileChooser;

		/*
		 * 
		 */
		private NewFileDialog newFileDialog;

		/*
		 * (non-Javadoc)
		 * 
		 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
		 */
		public void actionPerformed(ActionEvent e) {
			String command = e.getActionCommand();
			if (null == command) {
				return;
			}

			if (command.equals("new")) { //$NON-NLS-1$
				doNew();
			} else if (command.equals("open")) { //$NON-NLS-1$
				doOpen();
			} else if (command.equals("newaccount")) { //$NON-NLS-1$
				doNewAccount();
			} else if (command.equals("editaccountdetails")) { //$NON-NLS-1$
				Account account = null;
				e.getSource();

				doEditAccountDetails(account);
			}

		}

		/*
		 * 
		 */
		void doNew() {
			/*
			 * Display the new file dialog.
			 */
			if (null == this.newFileDialog) {
				this.newFileDialog = new NewFileDialog(FinanceFrame.this);
			}

			this.newFileDialog.setVisible(true);
			File file = this.newFileDialog.getFile();
			char[] password = this.newFileDialog.getPassword();

			if (null != file && null != password) {
				CabinetManager factory = new CabinetManager(file, password);
				this.newFileDialog.erasePasswords();

				FinanceApp.setCabinetManager(factory);
			}
		}

		/*
		 * 
		 */
		void doOpen() {
			/*
			 * Prompt the user for a file to open.
			 */
			JFileChooser fileDialog = getFileChooser();
			String curDir = FinanceFrame.this.prefs.get("FinanceFrame.OpenDirectory.Path", null); //$NON-NLS-1$
			fileDialog.setCurrentDirectory(null == curDir ? null : new File(curDir));

			if (JFileChooser.APPROVE_OPTION == fileDialog.showOpenDialog(FinanceFrame.this)) {
				File file = fileDialog.getSelectedFile();

				FinanceFrame.this.prefs.put(
						"FinanceFrame.OpenDirectory.Path", fileDialog.getCurrentDirectory().getAbsolutePath()); //$NON-NLS-1$

				/*
				 * Prompt the user for the password for the file.
				 */
				String response = JOptionPane
						.showInputDialog(
								FinanceFrame.this,
								MessageFormat.format(FinanceApp.getMessageResources().getString(
										"dialog.passwordprompt.message"), file.getName()), FinanceApp.getMessageResources().getString("dialog.passwordprompt.title"), JOptionPane.QUESTION_MESSAGE); //$NON-NLS-1$ //$NON-NLS-2$
				if (null != response) {
					char[] password = response.toCharArray();
					response = null;

					CabinetManager factory = new CabinetManager(file, password);
					FinanceApp.setCabinetManager(factory);
				}
			}
		}

		/*
		 * 
		 */
		void doNewAccount() {
			NewAccountDialog dialog = new NewAccountDialog(FinanceFrame.this);
			dialog.setVisible(true);

			Class<? extends Account> klass = dialog.getAccountType().getAccountClass();
			Account account = null;
			try {
				account = klass.newInstance();
			} catch (InstantiationException ex) {
				//
			} catch (IllegalAccessException ex) {
				//
			}
			account.setName(dialog.getAccountName());

			FinanceFrame.this.accountsTableModel.addAccount(account);

			FinanceApp.getCabinetManager().updateCabinet();
		}

		void doEditAccountDetails(Account account) {
			AccountDetailsDialog dialog = AccountDetailsDialog.createDialog(FinanceFrame.this, account);
			dialog.editAccount(account);
		}

		/*
		 * 
		 */
		private JFileChooser getFileChooser() {
			if (null == this.fileChooser) {
				this.fileChooser = new FinanceFileChooser();
			}

			return this.fileChooser;
		}
	}

	/*
	 * This ActionListener handles the exit action exclusively.
	 */
	private class ExitActionHandler implements ActionListener {

		/*
		 * (non-Javadoc)
		 * 
		 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
		 */
		public void actionPerformed(ActionEvent e) {
			/*
			 * Remember the state and bounds of the window for the next execution.
			 */
			rememberState();

			/*
			 * Close all resources.
			 */
			FinanceApp.setCabinetManager(null);

			/*
			 * Hide and dispose the window.
			 */
			setVisible(false);
			dispose();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.msb.finance.app.FinanceFileChangeListener#financeFileChanged(org.msb.finance.app.CabinetFactory)
	 */
	public void financeFileChanged(CabinetManager manager) {
		if (null == manager) {
			removeTabPane();
		} else {
			this.cabinet = manager.getCabinet();

			if (null == this.tabPane) {
				createTabPane();
			}

			addTabPane();
		}
	}
}
